use std::collections::HashSet;

use rstest::rstest;

use crate::tests::fixtures::rand_usize;
use crate::tiling;
use crate::tiling::{Direction, GridSlot};

#[rstest]
#[case::north_west_corner((2, 1), (-1, 0), Some(Some(0)))]
#[case::south_east_corner((3, 1), (0, 1), Some(Some(1)))]
#[case::border_tile_north((0, rand_usize(1, 3)), (0, -1), None)]
#[case::border_tile_east((rand_usize(1, 4), 3), (1, 0), None)]
#[case::border_tile_south((4, rand_usize(1, 3)), (0, 1), None)]
#[case::border_tile_west((rand_usize(1, 4), 0), (-1, 0), None)]
#[case::no_slot((2, 2), (-1, 0), Some(None))]
#[case::no_tile_id((2, 1), (0, 1), Some(Some(2)))]
fn test_get_neighbor_slot(
    #[case] current_slot: (usize, usize),
    #[case] offset: (isize, isize),
    #[case] expected_slot: Option<Option<usize>>,
) {
    // The expected slot needs to be &mut GridSlot, so direct parametrization does
    // not work, so this list of GridSlots is used instead.
    let mut expected_slots = vec![
        GridSlot {
            tile_id: Some(4),
            direction: Some(Direction::North),
            predecessor: Some((2, 1)),
            exposed_edges: vec![],
            excluded_connectors: HashSet::new(),
        },
        GridSlot {
            tile_id: Some(1),
            direction: Some(Direction::South),
            predecessor: Some((1, 1)),
            exposed_edges: vec![],
            excluded_connectors: HashSet::new(),
        },
        GridSlot {
            tile_id: None,
            direction: None,
            predecessor: Some((3, 2)),
            exposed_edges: vec![Direction::North],
            excluded_connectors: HashSet::new(),
        },
    ];
    let mut slot_grid = tiling::slot_grid::tests::create_test_slot_grid(false, false);
    slot_grid.current_slot = current_slot;

    let slot: Option<Option<&mut GridSlot>> = slot_grid.get_neighbor_slot(offset);
    assert_eq!(
        slot,
        match expected_slot {
            None => None,
            Some(slot_id) => {
                match slot_id {
                    None => Some(None),
                    Some(slot_id) => Some(Some(&mut expected_slots[slot_id])),
                }
            },
        }
    );
}
